﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using DreamIn.Structures.Utils;
namespace DreamIn.Structures.SearchEngine
{
    /// <summary>
    /// FileSearch class search for pictures on the local computer or given paths and returns
    /// a ThreadSafeList of Albums containg found pictures.
    /// </summary>
    public enum FileTimeType { CreationTime, LastAccessTime, LastWriteTime };
    public class FileSearch
    {
        String SearchPattern;
        List<Thread> searchThreads;
        bool CaseSensitive;
        private static int maxVisitDepth = 0;
        private static bool filterByDate = false;
        private static FileTimeType fileTimeType;
        private static DateTime startDateRange;
        private static DateTime endDateRange;
        private ThreadSafeList<Album> albums;
        public bool JoinThreads {get;set;}
        /// <summary>
        /// Initialize the FileSearch class.
        /// </summary>
        /// <param name="Dir"></param>
        /// <param name="SearchPattern"></param>
        /// <param name="CaseSensitive"></param>
        /// <param name="MaxVisitDepth"></param>
        /// <param name="_filterByDate"></param>
        /// <param name="_fileTimeType"></param>
        /// <param name="_startDateRange"></param>
        /// <param name="_endDateRange"></param>
        public FileSearch()
        {
            InitFileSearch("*.png,*.jpg,*.bmp,*.gif", false, 0, false, FileTimeType.CreationTime, new DateTime(), new DateTime());
        }
        /// <summary>
        /// Set custom SearchPattern.
        /// </summary>
        /// <param name="SearchPattern"></param>
        public void SetFileExtensions(String SearchPattern)
        {
            this.SearchPattern = SearchPattern;
        }
        /// <summary>
        /// Initialize class.
        /// </summary>
        private void InitFileSearch(String SearchPattern, bool CaseSensitive, int MaxVisitDepth, bool _filterByDate, FileTimeType _fileTimeType, DateTime _startDateRange, DateTime _endDateRange)
        {
            this.SearchPattern = SearchPattern;
            this.CaseSensitive = CaseSensitive;
            filterByDate = _filterByDate;
            fileTimeType = _fileTimeType;
            startDateRange = _startDateRange;
            endDateRange = _endDateRange;
            maxVisitDepth = MaxVisitDepth;
            albums = new ThreadSafeList<Album>();
        }
        /// <summary>
        /// Start searching.
        /// </summary>
        private void SearchDir(Object dir)
        {
            try
            {
                Regex FileExtensionDelim = new Regex(",");
                String[] FileExtensions = FileExtensionDelim.Split(SearchPattern);

                for (int i = 0; i < FileExtensions.Length; i++)
                {
                    FileExtensions[i] = FileExtensions[i].Trim();
                }


                GetFiles((String)dir, (String)dir, FileExtensions, CaseSensitive, 0);

            }
            catch (Exception)
            {
                // Update thread state.

            }
        }

        /// <summary>
        /// Get files.
        /// </summary>
        /// <param name="RootDir"></param>
        /// <param name="Dir"></param>
        /// <param name="FileExtensions"></param>
        /// <param name="CaseSensitive"></param>
        /// <param name="countVisitDepth"></param>
        private void GetFiles(string RootDir, string Dir, String[] FileExtensions, bool CaseSensitive, int countVisitDepth)
        {

            List<Picture> pictures = new List<Picture>();
            try
            {

                pictures.Clear();
                foreach (string FileExtension in FileExtensions)
                {
                    foreach (string File in Directory.GetFiles(Dir, FileExtension))
                    {
                        // Check if filter by date/.
                        if (filterByDate)
                        {
                            DateTime fdt = new DateTime();
                            FileInfo finfo = new FileInfo(File);
                            switch (fileTimeType)
                            {
                                case FileTimeType.CreationTime:
                                    fdt = finfo.CreationTime;
                                    break;
                                case FileTimeType.LastAccessTime:
                                    fdt = finfo.LastAccessTime;
                                    break;
                                case FileTimeType.LastWriteTime:
                                    fdt = finfo.LastWriteTime;
                                    break;
                            }
                            if (!((fdt.CompareTo(startDateRange.AddDays(-1)) > 0) && (fdt.CompareTo(endDateRange.AddDays(1)) < 0)))
                                continue;
                        }
                        FileInfo pictureFile = new FileInfo(File.ToString());
                        pictures.Add(new Picture(pictureFile.Name, pictureFile.FullName));
                    }
                }
            }
            catch (Exception)
            {
            }
            DirectoryInfo directoryInfo = new DirectoryInfo(Dir.ToString());
            if(pictures.Count>0)
            albums.Add(new Album(directoryInfo.Name, directoryInfo.FullName, pictures));
            
            // Check the visit depth.
            if (maxVisitDepth > 0)
            {
                countVisitDepth++;
                if (countVisitDepth == maxVisitDepth)
                    return;
            }

            // Recursively add all the files in the current directory's subdirectories.
            try
            {
                foreach (string D in Directory.GetDirectories(Dir))
                {
                    GetFiles(RootDir, D, FileExtensions, CaseSensitive, countVisitDepth);
                }
            }
            catch (Exception)
            {
            }
        }
        /// <summary>
        /// Get a ThreadSafeList Album for the given path.
        /// </summary>
        /// <param name="dir"></param>
        /// <returns></returns>
        public ThreadSafeList<Album> GetAlbums(String dir)
        {
            SearchDir(dir);
            return albums;
        }
        /// <summary>
        /// Get a ThreadSafeList with all Albums on the local computer.
        /// </summary>
        /// <returns></returns>
        public ThreadSafeList<Album> GetAllAlbums()
        {
            searchThreads = new List<Thread>();
            DiskCollection diskCollection = new DiskCollection();
            if (diskCollection.Load())
            {
                foreach (DiskCollection.LogicalDriveInfo driveInfo in diskCollection)
                {
                    Thread newThread = new Thread(new ParameterizedThreadStart(SearchDir));
                    newThread.Start(driveInfo.Name + ":\\");
                    if (JoinThreads)
                        newThread.Join();
                    searchThreads.Add(newThread);
                }
            }
            return albums;
        }
        /// <summary>
        /// Get a ThreadSafeList with all Albums on the given List of paths.
        /// </summary>
        /// <param name="dirList"></param>
        /// <returns></returns>
        public ThreadSafeList<Album> GetAllAlbums(List<String> dirList)
        {
            searchThreads = new List<Thread>();
            foreach (String dir in dirList)
            {
                Thread newThread = new Thread(new ParameterizedThreadStart(SearchDir));
                newThread.Start(dir);
                if(JoinThreads)
                    newThread.Join();
                searchThreads.Add(newThread);
            }
            return albums;
        }
        /// <summary>
        /// Check if FileSearch has completed.
        /// </summary>
        /// <returns></returns>
        public bool FileSearchCompleted()
        {
            if (JoinThreads) return true;
            foreach(Thread thread in searchThreads)
            {
                if (thread.IsAlive)
                    return false;
            }
            return true;
        }
    }
}
